package shan.rpc.yurpc.spi;


import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import shan.rpc.yurpc.serializer.Serializer;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SPI加载器(支持键值对映射)
 */
@Slf4j
public class SpiLoader {

    /**
     * 存储已加载的类：接口名=>(key=>实现类)
     */
    private static Map<String,Map<String,Class<?>>> loaderMap=new ConcurrentHashMap<>();

    /**
     * 对象实例缓存（避免重复new），类路径=>对象实例，单例模式
     */
    private static Map<String,Object> instanceCache=new ConcurrentHashMap<>();

    /**
     * 系统默认SPI目录
     */
    private static final String RPC_SYSTEM_SPI_DIR="META-INF/rpc/system/";

    /**
     * 用户自定义SPI目录
     */
    private static final String RPC_CUSTOM_SPI_DIR="META-INF/rpc/custom/";

    /**
     * 扫描路径
     */
    private static final String[] SCAN_DIRS={RPC_SYSTEM_SPI_DIR,RPC_CUSTOM_SPI_DIR};

    /**
     * 动态加载类列表
     */
    private static final List<Class<?>> LOAD_CLASS_LIST= Arrays.asList(Serializer.class);

    /**
     * 加载所有类型
     */
    public static void loadAll() {
        log.info("start load all spi");
        for(Class<?> aClass:LOAD_CLASS_LIST){
            load(aClass);
        }
    }

    /**
     * 获取某个接口的实例
     */
    public static <T> T getInstance(Class<?> tClass,String key){
        String tClassName=tClass.getName();
        Map<String,Class<?>> keyClassMap=loaderMap.get(tClassName);
        if(keyClassMap==null){
            throw new RuntimeException(String.format("SpiLoader未加载%s类型",tClassName));
        }
        if(!keyClassMap.containsKey(key)){
            throw new RuntimeException(String.format("SpiLoader的%s不存在key=%s类型",tClassName,key));
        }
        //获取到要加载的实现类型
        Class<?> implClass=keyClassMap.get(key);
        //从实例缓存中加载的实现类型
        String implClassName=implClass.getName();
        if(!instanceCache.containsKey(implClassName)){
            try{
                instanceCache.put(implClassName,implClass.newInstance());
            }catch (InstantiationException | IllegalAccessException e){
                String errorMsg=String.format("SpiLoader加载%s实例失败",implClassName);
                throw new RuntimeException(errorMsg,e);
            }
        }
        return (T) instanceCache.get(implClassName);
    }

    /**
     * 加载某个类型
     */
    public static Map<String,Class<?>> load(Class<?> loadClass){
        log.info("加载类型为{}的SPI",loadClass.getName());
        //扫描路径，用户自定义的SPI优先级高于系统SPI
        Map<String,Class<?>> keyClassMap=new HashMap<>();
        for(String scanDir:SCAN_DIRS){
            List<URL> resources= ResourceUtil.getResources(scanDir+loadClass.getName());
            for(URL resource:resources){
                try{
                    InputStreamReader inputStreamReader=new InputStreamReader(resource.openStream());
                    BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
                    String line;
                    while((line=bufferedReader.readLine())!=null){
                        String[] strArrays=line.split("=");
                        if(strArrays.length>1){
                            String key=strArrays[0];
                            String className=strArrays[1];
                            keyClassMap.put(key,Class.forName(className));
                        }
                    }
                }catch (Exception e){
                    log.error("spi resource load error",e);
                }
            }
        }
        loaderMap.put(loadClass.getName(),keyClassMap);
        return keyClassMap;
    }

}
